// Avoid `console` errors in browsers that lack a console.
(function() {
  var method;
  var noop = function () {};
  var methods = [
  'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
  'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
  'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
  'timeStamp', 'trace', 'warn'
  ];
  var length = methods.length;
  var console = (window.console = window.console || {});

  while (length--) {
    method = methods[length];

    // Only stub undefined methods.
    if (!console[method]) {
      console[method] = noop;
    }
  }
}());
   
// Place any jQuery/helper plugins in here.
;
(function($,undefined){
  $.fn.gridTile = function(options) {
    var settings = $.extend({
      width: 500,
      height: 500,
      rows: 10,
      cols: 10,
      tileWidth: 80,
      tileHeight: 80,
      zoom: 100,
      zoomStep: 3,
      selected_material: -1,
      clipboardMemoryLimit: 500 //kilobyte
    }, options );
    var obj = this; //The element that user applied for.
    obj.append('<div class="grid_container"></div>');
    //grid: Grid contain the table, fit the object when
    //table's sizeis smaller than obj, and wrap around the table
    //when the table's size is bigger
    var grid = obj.find('.grid_container');
    var default_zoom = settings.zoom;
    //Current size of the table
    var current_height = 0;
    var current_width = 0;

    // Table object
    var table_obj;

    //Material div
    var material_obj;
    var material_tiles = Array();
    myclipboard = function() {
      var data = [];
      var cursor = -1;
      this.load = function() {
        try {
          data = JSON.parse(localStorage["dongtam_clipboard"]);
          cursor = Number(localStorage["dongtam_cursor"]);
          
        } catch (e) {
          data = [];
        }
      }
      this.ready = function() {
        return (data.length > 0);
      }
      this.clipboardToJSON = function() {
        return JSON.stringify(data);
      }
      this.save = function() {
        localStorage["dongtam_clipboard"] = this.clipboardToJSON();
        localStorage["dongtam_cursor"] = cursor;
      }
      this.get = function() {
        if (cursor == -1)　return null;
        return data[cursor];
      }
      this.add = function(d) {
        if (d == data[data.length -1]) {
          return data[cursor];
        }

        if (cursor + 1 < data.length) {
          cursor++;
          data[cursor] = d;
        } else {
          cursor++;
          data.push(d);
        }
        while (this.memory() > settings.clipboardMemoryLimit) {
          data.splice(0, 1);
          cursor--;
          this.save();
        }

        data.splice(cursor + 1, data.length - cursor - 1);
        this.save();
        return data[cursor];
      }
      this.back = function() {
        if (cursor - 1 > -1) {
          cursor--;
        }
        this.save();
        return data[cursor];
      }
      this.next = function() {
        if (cursor + 1 < data.length) {
          cursor++;
        }
        this.save();
        return data[cursor];
      }
      this.memory = function() {
        //http://stackoverflow.com/questions/4391575/how-to-find-the-size-of-localstorage
        var allStrings = '';
        for(var key in window.localStorage){
          if(window.localStorage.hasOwnProperty(key)){
            allStrings += window.localStorage[key];
          }
        }
        return allStrings ? 3 + ((allStrings.length*16)/(8*1024)):0;
      }
      this.destroy = function() {
        data = [];
        localStorage["dongtam_clipboard"] = "";
        return true;
      }
      return this;
    }
 
    var clipboard = myclipboard();

    init = function() {
      //Set the element width and height.
      obj.css({
        'width': settings.width,
        'height': settings.height,
        'overflow':'hidden'
      }).addClass('grid_tiles');

      
      getTableObj = function() {
        return table_obj;
      }
      clearGrid = function() {
        //Clear the grid
        grid.html('');
      }
      setMaterialToTile = function(tile, material, use_clipboard) {
        tile.addClass( "dropped" );
        //Generate a IMG with dropped element's canvas.
        tile.find('img').attr('src',material.find('img').attr('src'));
        tile.attr('material_id', material.attr('material_id'));
        if (typeof use_clipboard != "undefined") { // if there is a 3th para
          if (use_clipboard) { //if 3th para is true
            saveToClipboard();
          }
        } else { //if there is no 3th para
          saveToClipboard();
        }
      }

      createTable = function(rows, cols) {
        clearGrid();
        //This generate the table with IMG tag inside TDs.
        var str = '<table id="grid_tile">';
        for (i = 0; i < rows; ++i) {
          str += '<tr>';
          for (j = 0; j < cols; ++j) {
            str += '<td><img/></td>';
          }
          str += '</tr>';
        }
        str += '</table>';

        //Append the table.
        grid.append(str);
        table_obj = grid.find('table#grid_tile');
        //Re-set the size
        setTileSize();
        bindDropEvent();
        //After the table have been created
        //Center the table
        centerTable();
        //Return the table
        return table_obj;
      }
      bindDropEvent = function() {
        $("table#grid_tile").draggable();
        //Create event to receive drop event.
        $( "table#grid_tile tr td" ).droppable({
          //Only accept #draggable element
          accept: ".tile_draggable",
          hoverClass: "hoverwaitting",
          drop: function( event, ui ) {
            //check if the dropped tile is in view
            //console.log("[dropped] top:" + $(this).offset().top + " left:" + $(this).offset().left + " width:" + $(this).width() + " height:" + $(this).height() );
            //console.log("[view]  top:" + obj.offset().top + " left:" + obj.offset().left + " width:" + obj.width() + " height:" + obj.height() );
            if ($(this).offset().top + $(this).height() > obj.offset().top &&
              $(this).offset().left + $(this).width() > obj.offset().left &&
              $(this).offset().top < obj.offset().top + obj.height() &&
              $(this).offset().left < obj.offset().left + obj.width()) {

              setMaterialToTile($(this), ui.draggable);
            }
          }
        });
      }
      bindTileSelectable = function() {
      /*
        table_obj.on('click', 'td', function() {
          $(this).toggleClass('selecting');
        });
         */
      }
      bindKeyboardShortcut = function() {
        var keyboardShortcutDelay;
        var keyDelay = true;
        $('body').keydown(function(e) {
          if (e.ctrlKey){
            if (e.keyCode == 90) {
              if (keyDelay) {
                keyDelay = false;
                undo();
              }
              clearTimeout(keyboardShortcutDelay);
              keyboardShortcutDelay = setTimeout(function() {
                keyDelay = true;
              }, 100);
            }
            if (e.keyCode == 89) {
              if (keyDelay) {
                keyDelay = false
                redo();
              }
              clearTimeout(keyboardShortcutDelay);
              keyboardShortcutDelay = setTimeout(function() {
                keyDelay = true;
              }, 100);
            }
          }

        });
      }
      bindTilePaintable = function() {
        var mouse_moved = false;
        var _old = 0;
        table_obj.on('click', 'td', function(e) {
          if (settings.selected_material > -1 && !mouse_moved) {
            //Generate a IMG with selected material's canvas.
            var target_material = $('.material_space .tile_draggable[material_id="'+settings.selected_material+'"]');
            setMaterialToTile($(this), target_material);
          }
        }).on('mousedown', 'td', function(e) {
          mouse_moved = false;
          _old = e.clientX + ":" + e.clientY;
        }).on('mouseup', 'td', function(e) {
          if (_old != e.clientX + ":" + e.clientY) {
            mouse_moved = true;
          }
        });
      }
      setTileSize = function() {
        current_width = settings.tileWidth + settings.zoom-100;
        current_height = settings.tileHeight + settings.zoom-100;
        table_obj.css({
          width:(current_width * settings.cols),
          height:(current_height * settings.rows)
        });
        //console.log((width * settings.cols) + ' ' + (height * settings.rows));
        grid.find('tr').css('height', current_height);
        grid.find('img').css({
          'width': current_width,
          'height': current_height
        });
        grid.find('td').css({
          'width': current_width,
          'height': current_height
        });
      }
      //Center the table;
      centerTable = function() {
        table_obj.css({
          left: ((-table_obj.width() + settings.width)/2),
          top: ((-table_obj.height() + settings.height)/2)
        });
        return table_obj;
      }
      zoomTo = function(value) {
        settings.zoom = default_zoom + Number(value);
        re_position_table();
      }
      zoomIn = function() {
        //if (settings.tileWidth + settings.zoom-100 > 10 && settings.tileHeight + settings.zoom-100 > 10) {
        settings.zoom += settings.zoomStep;
        re_position_table();
      //}
      }
      zoomOut = function() {
        if (settings.tileWidth + settings.zoom-100 > 30 && settings.tileHeight + settings.zoom-100 > 30) {
          settings.zoom -= settings.zoomStep;
          re_position_table();
        }
      }
      re_position_table = function() {
        //console.log((table_obj.position().left - obj.offset().left) + " : " + (table_obj.position().top - obj.offset().top));
        //console.log('obj offset' + obj.offset().left);
        //console.log((table_obj.position().left - obj.offset().left) + " : " + (table_obj.position().top - obj.offset().top));

        //Some magic happened here...

        var old_width = table_obj.width();
        var old_height = table_obj.height();
        var _xa = (obj.width() / 2) - (table_obj.position().left - obj.offset().left);
        var _x = table_obj.width();
        var _ya = (obj.height() / 2) - (table_obj.position().top - obj.offset().top);
        var _y = table_obj.height();
        setTileSize();
        var distance_w = (old_width - table_obj.width());
        var distance_h = (old_height - table_obj.height());

        table_obj.css({
          'left':table_obj.position().left - obj.offset().left + distance_w *Math.round(100* _xa / _x)/100,
          'top':table_obj.position().top - obj.offset().top + distance_h *Math.round(100* _ya / _y)/100
        });
      }
      activeWheelZoom = function () {
        obj.mousewheel(function(event, delta, deltaX, deltaY) {
          //console.log(delta, deltaX, deltaY);
          event.preventDefault();
          if (delta == 1) {
            zoomIn();
          } else if (delta == -1) {
            zoomOut();
          }
        });
      }
      removeRowAbove = function(num) {
        if (settings.rows - num <= 0) {
          return;
        }
        for (i = 0; i< num; ++i) {
          table_obj.find('tr').first().remove();
        }
        table_obj.css('top', table_obj.position().top - obj.offset().top + num*current_height);
        settings.rows -= num;
        setTileSize();
        saveToClipboard();
      }
      removeRowBelow = function(num) {
        if (settings.rows - num <= 0) {
          return;
        }
        for (i = 0; i< num; ++i) {
          table_obj.find('tr').last().remove();
        }
        settings.rows -= num;
        setTileSize();
        saveToClipboard();
      }
      removeColLeft = function(num) {
        if (settings.cols - num <= 0) {
          return;
        }
        table_obj.find('tr').each(function() {
          for (i = 0; i < num; ++i) {
            $(this).find('td').first().remove();
          }
        });
        settings.cols -=num;
        table_obj.css('left', table_obj.position().left - obj.offset().left + num*current_width);
        setTileSize();
        saveToClipboard();
      }
      removeColRight = function(num) {
        if (settings.cols - num <= 0) {
          return;
        }
        table_obj.find('tr').each(function() {
          for (i = 0; i < num; ++i) {
            $(this).find('td').last().remove();
          }
        });
        settings.cols -=num;
        setTileSize();
        saveToClipboard();
      }
      addColLeft = function(num) {
        var str = '';
        for (i = 0; i < num; ++i) {
          str += '<td><img/></td>';
        }
        table_obj.find('tr').each(function() {
          $(this).prepend(str);
        });
        settings.cols +=num;
        table_obj.css('left', table_obj.position().left - obj.offset().left - num*current_width);
        setTileSize();
        bindDropEvent();
        saveToClipboard();
      }
      addColRight = function(num) {
        var str = '';
        for (i = 0; i < num; ++i) {
          str += '<td><img/></td>';
        }
        table_obj.find('tr').each(function() {
          $(this).append(str);
        });
        settings.cols +=num;
        setTileSize();
        bindDropEvent();
        saveToClipboard();
      }
      addRowAbove = function(num) {
        var str = '';
        for (i = 0; i < num; ++i) {
          str += '<tr>';
          for (j = 0; j < settings.cols; ++j) {
            str += '<td><img/></td>';
          }
          str += '</tr>';
        }
        settings.rows +=num;
        table_obj.css('top', table_obj.position().top - obj.offset().top - num*current_height);
        table_obj.prepend(str);
        setTileSize();
        bindDropEvent();
        saveToClipboard();
      }
      addRowBelow = function(num) {
        var str = '';
        for (i = 0; i < num; ++i) {
          str += '<tr>';
          for (j = 0; j < settings.cols; ++j) {
            str += '<td><img/></td>';
          }
          str += '</tr>';
        }
        settings.rows +=num;
        table_obj.append(str);
        setTileSize();
        bindDropEvent();
        saveToClipboard();
      }

      init_material_space = function() {
        material_obj = $(settings.material_space);
        draggableMaterials();
        materialSelectable();
        return material_obj;
      }
      clearMaterialSpace = function (){
        material_obj.html('');
        material_tiles = [];
      }
      draggableMaterials = function() {
        material_obj.find( ".tile_draggable" ).draggable({
          start: function( event, ui ) {
            //console.log($(this).find('canvas').height() + " : " + $(this).find('canvas').width());
            //ui.helper.html('<img src="'+$(this).find('canvas')[0].toDataURL()+'"/>');
            ui.helper.addClass('drop-shadhow');
          },
          opacity: 0.5,
          helper: "clone"
        });
      }
      addMaterial = function(dataURL) {
        var new_tile_id = material_tiles.length;
        material_obj.append('<div class="tile_draggable" id="material_'+new_tile_id+
          '" material_id="'+new_tile_id+'"><img src="'+dataURL+'"/></div>');
        
        material_tiles.push(dataURL);
        draggableMaterials();
        saveToClipboard();
        return material_tiles[new_tile_id];
      }
      materialSelectable = function() {
        material_obj.on('click', '.tile_draggable', function() {
          //set the cursor icon
          if ($(this).hasClass('selecting')) {
            $(this).removeClass('selecting');
            settings.selected_material = -1;
          } else {
            material_obj.find('.tile_draggable').removeClass('selecting');
            $(this).addClass('selecting');
            settings.selected_material = Number($(this).attr('material_id'));
          }
        });
      }
      selectedMaterial = function () {
        return settings.selected_material;
      }
      toJSON = function() {
        var _tile_map = Array();
        var i = 0, j = 0;
        table_obj.find('tr').each(function() {
          var tr_obj = $(this);
          i++;
          j = 0;
          tr_obj.find('td').each(function() {
            j++;
            var td_obj = $(this);

            if (td_obj.attr('material_id')) {
              _tile_map.push({
                'position':{
                  row: i,
                  col: j
                },
                'material_id':td_obj.attr('material_id')
              });
            }
          });
        });

        var _material_map = Array();
        for (i = 0; i < material_tiles.length; i++) {
          _material_map.push({
            id:i,
            stage:material_tiles[i]
          });
        }

        var result = {
          wp_settings: settings, //wp_ = working space, NOT wordpress!
          tile_map: _tile_map,
          material_map: _material_map
        };
        
        return JSON.stringify(result);
      }
      loadJSON = function(json_str) {
        var working_space;
        try {
          working_space = JSON.parse(json_str);

        } catch(e) {
          console.log("Could not parse json_str");
          console.log(e);
          return;
        }
        //Clear things
        clearMaterialSpace();
        clearGrid();

        //Load settings and re-create table
        settings = JSON.parse(JSON.stringify(working_space.wp_settings));
        createTable(settings.rows, settings.cols);
        var stage;

        //Re-create material_space
        for (i = 0; i < working_space.material_map.length; ++i) {
          //addMaterial(canvas_width, canvas_height, layer)
          material_obj.append('<div class="tile_draggable" id="material_'+i+'" material_id="'+i+'"><img src="'+working_space.material_map[i].stage+'"/></div>');
          //stage = Kinetic.Node.create(working_space.material_map[i].stage, 'material_'+i);
          material_tiles.push(working_space.material_map[i].stage);
        }
        //re-select prev material
        $('.material_space .tile_draggable[material_id="'+settings.selected_material+'"]').addClass('selecting');

        draggableMaterials();

        //Load tile_map
        for (i = 0; i < working_space.tile_map.length; ++i) {
          var cell_obj = $($(table_obj.find('tr')[working_space.tile_map[i].position.row - 1]).find('td')[working_space.tile_map[i].position.col - 1]);
          var target_material = $('.material_space .tile_draggable[material_id="'+working_space.tile_map[i].material_id+'"]');
          setMaterialToTile(cell_obj, target_material, false);
        }

        bindTilePaintable();
      }
      saveToClipboard = function() {
        clipboard.add(this.toJSON());
      }
      getClipboard = function() {
        return clipboard;
      }
      clearClipboard = function() {
        return clipboard.destroy();
      }
      undo = function() {
        this.loadJSON(clipboard.back());
      }
      redo = function() {
        this.loadJSON(clipboard.next());
      }
      resetWorkingSpace = function() {
        clearClipboard();
        location.reload();
      }
      usedMemory = function() {
        return clipboard.memory();
      }

      init_material_space();
      table_obj = createTable(settings.rows, settings.cols);
      bindTileSelectable();
      bindTilePaintable();
      bindKeyboardShortcut();
      activeWheelZoom();

      clipboard.load();
      if (clipboard.ready()) {
        this.loadJSON(clipboard.get());
      }
      return this;
    }

    return init();
  }
})(jQuery)